package astudy.compile;

import java.io.File;
import java.io.FileInputStream;
import java.io.PushbackInputStream;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;

/**
 * 
 * @author wong.tong@gmail.com
 *
 */
public class Main {

	private static final int BSIZE 	= 128;
	private static final int NONE 	= -1;
	
	private static final int NUM 	= 256;
	private static final int DIV 	= 257;
	private static final int MOD 	= 258;
	private static final int ID 	= 259;
	private static final int DONE	= 260;

	private static class Entry extends HashMap<String, Integer> {

		private static final long serialVersionUID = 3177021384284248774L;
	}

	private static Entry symtable;
	private static int tokenval;
	private static int lineno;
	private static PushbackInputStream pis;
	
	/**
	 * start -> list eof
	 * ...
	 * ...
	 * 
	 * @param args
	 */
	public static void main(String[] args) throws Exception {

		URL url = Main.class.getResource("script.txt");
		File file = new File(URLDecoder.decode(url.getPath(), "UTF-8"));
		pis = new PushbackInputStream(new FileInputStream(file));

		init();
		parse();
		
		pis.close();
		System.exit(0);
	}

	/**
	 * 词法分析
	 * @return
	 */
	private static int lexan() throws Exception {

		int t;
		while (true) {
			t = pis.read();
			if (t == ' ' || t == '\t') {
			} else if (t == '\r') {
			} else if (t == '\n') {
				lineno ++;
			} else if (isdigit(t)) {
				tokenval = t-'0';
				t = pis.read();
				while (isdigit(t)) {
					tokenval = tokenval*10 + t-'0';
					t = pis.read();
				}
				pis.unread(t);
				return NUM;
			} else if (isalpha(t)) {
				String lex = "";
				while (isalpha(t) || isdigit(t)) {
					lex += (char)t;
					t = pis.read();
					if (lex.length() > BSIZE) {
						error("syntax error");
					}
				}
				//go on
				if (symtable.get(lex) == null) {
					symtable.put(lex, ID);
				}
				return symtable.get(lex);
			} else if (t == -1) {
				return DONE;
			} else {
				tokenval = NONE;
				return t;
			}
		}
	}
	
	private static boolean isdigit(int t) {
		return t >= '0' && t <= '9';
	}

	private static boolean isalpha(int t) {
		return (t >= 'a' && t <= 'z') || (t >= 'A' && t <= 'Z');
	}

	static int lookahead; 
	/**
	 * 分析并翻译表达式列表
	 */
	private static void parse() throws Exception {
		
		lookahead = lexan();
		while (lookahead != DONE) {
			expr();
			match(';');
		}
	}

	private static void expr() throws Exception {
		
		int t;
		term();
		while (true) {
			switch (lookahead) {
			case '+':
			case '-':
				t = lookahead;
				match(lookahead);
				term();
				emit(t, NONE);
				continue;
			default:
				return;
			}
		}
	}

	private static void term() throws Exception {
		
		int t;
		factor();
		while (true) {
			switch (lookahead) {
			case '*':
			case '/':
			case DIV:
			case MOD:
				t = lookahead;
				match(lookahead);
				factor();
				emit(t, NONE);
				continue;
			default:
				return;
			}
		}
	}

	private static void factor() throws Exception {

		switch (lookahead) {
		case '(':
			match('(');
			expr();
			match(')');
			break;
		case NUM:
			emit(NUM, tokenval);
			match(NUM);
			break;
		case ID:
			emit(ID, tokenval);
			match(ID);
			break;
		default:
			error("syntax error");
		}
		
	}

	private static void match(int t) throws Exception {

		if (lookahead == t) {
			lookahead = lexan();
		} else {
			error("syntax error");
		}
	}
	
	/**
	 * 生成输出
	 * @param t
	 * @param tval
	 */
	private static void emit(int t, int tval) {
		
		switch (t) {
		case '+':
		case '-':
		case '*':
		case '/':
			System.out.println((char)t);
			break;
		case DIV:
			System.out.println("DIV");
			break;
		case MOD:
			System.out.println("MOD");
			break;
		case NUM:
			System.out.println(tval);
			break;
		case ID:
			System.out.println(symtable.get(tval));
			break;
		default:
			System.out.println("token: " + t + ", token-value: " + tval);
		}
	}
	
	private static void error(String string) {
		
		try {
			throw new RuntimeException(string);
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	private static void init() {
		
		symtable = new Entry();
		//add keywords
		symtable.put("div", DIV);
		symtable.put("mod", MOD);
	}
	
}
